#include "header.h"
#include <new>
#include <iostream>

EventCalendar::EventCalendar() {
	head = NULL;
}

EventCalendar::~EventCalendar() {
	eventNode *ptr;
	while ( head != NULL) {
		ptr = head;
		head = head->next;
		try {
			delete ptr;
		}
		catch (...) {
			throw MEMERROR_FREE;
		}
	}
}

void EventCalendar::addEvent(event e) {
	eventNode *ptr;
	ptr = head;
	if (head == NULL || (head->content).time > e.time) {
		try {
			head = new eventNode;
		}
		catch (std::bad_alloc &e) {
			throw MEMERROR_ALLOC;
		}
		head->content = e;
		head->next = ptr;
		return;
	}
	while (ptr->next != NULL && (ptr->next->content).time < e.time) {
		ptr = ptr->next;
	}
	eventNode *tmp;
	try {
		tmp = new eventNode;
	}
	catch (std::bad_alloc &e) {
		throw MEMERROR_ALLOC;
	}
	tmp->next = ptr->next;
	ptr->next = tmp;
	tmp->content = e;
	return;
}

event EventCalendar::popNextEvent() {
	if (head == NULL) {
		throw LOGICERROR_EVENTLISTEMPTY;
	}
	eventNode *p;
	event r;
	p = head;
	head = p->next;
	r = p->content;
	try {
		delete p;
	}
	catch (...) {
		throw MEMERROR_FREE;
	}
	return r;
}

void Scenario::assign2Queue(entity e, unsigned queueId, double time) {
	queueMgr.getQueue(queueId).enqueue(e,time);
}

void Scenario::assign2Server(entity e, unsigned serverId, double time) {
	Server s;
	event ev;
	s = serverMgr.getServer(serverId);
	s.enserver(e,time);
	ev.server = s;
	ev.time = time + s.serviceDuration();
	ev.type = ServiceFinish;
	eventCalendar.addEvent(ev);
}

Scenario::Scenario() {
	report = false;
}

Scenario::~Scenario() {
	if ( report ) {
		std::cout << "=============Syetem Statistics===========" << std::endl;
		std::cout << "total.time       = " << clock << std::endl;
		std::cout << "total.balked     = " << popMgr.balkCount() << std::endl;
		std::cout << "total.served     = " << serverMgr.totalServed() << std::endl;
		std::cout << "total.entities   = " << popMgr.getCount() << std::endl;
		std::cout << "avg.service.time = " << serverMgr.avgServiceTime() << std::endl;
		std::cout << "avg.waiting.time = " << queueMgr.avgWaitingTime() + serverMgr.avgServiceTime() << std::endl;
	}
}

void Scenario::run() {
	int i;
	init();
	if ( file ) {
		popMgr.report = false;
		popMgr.file = file;
	}
	else {
		popMgr.report = report;
	}
	queueMgr.report = report;
	serverMgr.report = report;
	event ev;
	ev.time = arrivalDuration();
	ev.type = Arrival;
	eventCalendar.addEvent(ev);
	clock = 0;
	while (true) {
		if (termCondition()) {
			serverMgr.term(clock);
			queueMgr.term(clock);
			std::cout << "\nSimulation stopped at time "<< clock << ".\n" << std::endl;
			return;
		}
		ev = eventCalendar.popNextEvent();
		clock = ev.time;
		if (ev.type == Arrival) {
			event newev;
			newev.time = ev.time + arrivalDuration();
			newev.type = Arrival;
			eventCalendar.addEvent(newev);
			arrivalHandler(popMgr.newEntity());
		}
		else {
			serviceFinishHandler(ev.server);
		}
	}
}

void Scenario::init() {
	report = arguments.report;
	file = arguments.file;
	queueMgr.newQueue(arguments.queueType, arguments.capacity);
}

bool Scenario::termCondition() {
	return serverMgr.totalServed() >= arguments.termCondition;
}

void Scenario::arrivalHandler(entity en) {
	Server server;
	server = serverMgr.getServer(0);
	assign2Queue(en,0,clock);
	if (server.isIdle() == true) {
		assign2Server(queueMgr.getQueue(0).dequeue(clock),0,clock);
	}
}
void Scenario::serviceFinishHandler(Server server) {
	Queue queue;
	server.deserver(clock);
	queue = queueMgr.getQueue(0);
	if (queue.getCount() >= 1) {
		assign2Server(queue.dequeue(clock),server.getId(),clock);
	}
}

double Scenario::arrivalDuration() {
	return expoRand(arguments.mu);
}
